Using Modeless Dialogs as MDI Child Windows ------------------------------------------- This file contains important information for using the TPW MDITypes unit. You should read this before using the unit. As of this writing (11/30/91 at 3:43 PM), the code works well. There were problems with sporadic UAEs in my previeous testing, but these seem to have been resolved. The upshot of the exercise has been to follow these rules: 1. *NEVER* change the style bits of an MDI child window after it has been created. This makes Windows unstable. 2. *NEVER* delete menu items from the system menu of the MDI child window. You can disable menu items, but deleting them seems to lead to further Windows instability. 3. If you want to prevent a windoe from changing size, handle wm_SysCommand and check for Msg.wParam = sc_Size, sc_Maximize, and/or wm_Minimize. Return 1 in Msg.Result if you see these values, otherwise call DefWndProc(Msg). The program now seems steady as a rock. The MDI child windows containing dialog boxes don't look exactly like I wanted them to look, but the program doesn't crater Windows any more. I'm willing to live with the aesthetics for now. The archive file contains the following files: MDIEXAM ACL 237 Test program accelerators MDIDLG1 DLG 1185 Test program MDI child dialog #1 MDIDLG2 DLG 578 Test program MDI child dialog #2 DIALOG1 ICO 1846 Icon for MDI child dialog #1 windows DIALOG2 ICO 1846 Icon for MDI child dialog #2 windows MDIEDIT ICO 1846 Icon for MDI child edit windoww MDIEXAM ICO 1846 Icon for MDI test program MDIEXAM INC 735 Include file for the test program MDIEDIT MNU 802 Dialog & edit child windows menu MDIEXAM MNU 502 Frame menu for case of no children MDIRECT MNU 535 Random rectangle child windows menu MDIEXAM PAS 44832 Test program source code MDITYPES PAS 27672 MDI child window unit source code MDIEXAM RC 328 Test program RC file. For use with RW or WRT! The MDITypes unit is the heart of the implementation. There are three different object types defined in the unit, as follows: TMDIChild: TMDIChild descends from TWindow. The type supports changing the current menu displayed by the frame window when an MDI child window is activated or deactivated. To accomplish this end, the type defines a response method for the wm_MDIActivate message. TMDIDialog: This type descends from TMDIChild. It supports the concept of a modeless dialog as an MDI child window. TMDIFrame: This type is a descendant of TMDIWindow. It defines three methods that were not included TMDIWindow. These allow you to find the OWL object associated with an MDI child window handle (GetChild(TheWnd: HWnd)); closing a specific child window (TMDIWindow only supports closing all child windows; to destroy a single window, you have to double click on the child's system menu); and a wm_InitMenu reponse method to simplify the process of allowing an MDI child window to modify the state of the current menu. This is useful for those cases where you have an Edit menu, for example, and you need to gray items on it based on whether or not you can undo an edit, paste, cut, etc. The source code is heavily commented, so you should be able to follow all that is going on in it. Any questions should be directed to me, Tony Vitabile 76054, 1450 on BPROGA section 8. USING MULTIPLE MENUS IN AN MDI PROGRAM -------------------------------------- Being able to have a different menu be displayed when a particular type of MDI child window is currently active is a major feature of the MDI specification. However, OWL doesn't provide any support for this feature. This isn't a difficult feature to support, but it would be easier if there were an object type that encapsulated all the work involved for you. Now there is such an object type: TMDIChild. Here's how you use it. Define an object type in your program that descends from TMDIChild. This allows you to add other functionality your window may need in addition to that provided by TMDIChild. In your Init constructor, you provide two additional pieces of information that the TMDIChild window will need to support switching the frame menu. These are: - A handle to the menu to be displayed when this window is active; - The position number of the Window popup menu item on the menu. TMDIChild stores this information in two fields, then determines the menu handle of the Window popup menu and saves that as well. That's all you have to do. From here on, changing the menu is automatically handled by TMDIChild's methods. When a TMDIChild receives a wm_MDIActivate message, the wParam field tells the window whether it's being activated or deactivated. TMDIChild uses this information to determine whether it should switch to the frame window's default menu (the one included in the frame window's Init constructor) or the one whose handle is contained in this instance of TMDIChild. The wm_MDIActivate response method retrieves the appropriate handles from the appropriate objects, then sends the wm_MDIChangeMenu message to the MDI Client window. After that, the job is done. Note that Load & Store methods are provided for TMDIChild so that you can save descendants and instances of this type on streams. See the section at the end of this README.TXT file for more information regarding the proper streaming of TMDIChild & its descendants. USING A MODELESS DIALOG AS AN MDI CHILD WINDOW ---------------------------------------------- So you want to use a modeless dialog box as an MDI child window? Well, it's not quite as easy as it might seem, nor is it as difficult as you may think. The MDITypes unit was written to provide a basis for supporting this type of feature in your programs. All the work involved in maintaining a window of this type is encapsulated in the type TMDIDialog. The paragraphs that follow describe how you extend this type to include your dialog box. TMDIDialog is an abstract type. A window of type TMDIDialog itself should never exist. This is because TMDIDialog is a generic type and does not have a predefined dialog template that it uses. YOUR code supplies that template. After you make the dialog known, TMDIDialog's methods then do all the work. TMDIDialog defines a new method called InitDialog. This is a function that returns a pointer to an object of type TDlgWindow. To create your own MDI dialog box type, you descend from TMDIDialog and then override InitDialog. InitDialog should call New, passing the constructor of an object descended from type TDlgWindow. The value returned by New is the value you return to InitDialog's caller. For example: type MyDialog = object (TDlgWindow) . . . end; MyMDIDialog = object (TMDIDialog) . . . function InitDialog: PDlgWindow; virtual; . . . end; . . . function MyMDIDialog.InitDialog: PDlgWindow; begin { MyMDIDialog.InitDialog } InitDialog := PDlgWindow(New(PMyDialog, Init(...))) end { MyMDIDialog.InitDialog }; That's it! From here on, TMDIDialog takes care of all the details. Of course, if you want your MDI dialog type to do more, you'll have to add methods to cover those situations. The sample program included in this archive, MDIEXAM.PAS, shows how to create 2 different types of MDI dialog child windows. STREAMING TMDICHILD DESCENDANTS ------------------------------- The Load & Store methods for both TMDIChild & TMDIDialog take pretty simplistic approaches to reading & writing windows of these types on streams. They assume the value written to the stream for a menu handle will be valid when the window is read. This is *NOT* true, but there is nothing that TMDIChild or TMDIDialog can do about about it, since they have no way of knowing the resource name associated with that menu. If you intend to stream your TMDIChild and/or TMDIDialog descendants, you should put some code in your descendant's Load/Store method to make sure that the menu handles in your windows are correct. For example: constructor MyMDIChild.Load(var S: TStream); begin { MyMDIChild.Load } { Get the window from the stream } TMDIChild.Load(S); { Now fix up the menus } Menu := LoadMenu(HInstance, mymenu); { or use a global var } WndMenu := GetSubMenu(Menu, MenuPos) { Set windo menu properly} end { MyMDIChild.Load }; This will properly initialize the menu handles for the MDI child window. It is important that you set WndMenu too! Failure to do so will have unpredictable results, and may crash Windows. If your window is a descendant of MDIDialog, you will need to implement code like that above. Make sure you call TMDIDialog.Load instead of TMDIChild.Load. TMDIDialog.Load calls TMDIChild.Load and performs additional steps required for the type. COPYRIGHT --------- The program and unit source code included in this archive are Copyright (c) 1991 by Anthony M. Vitabile. All rights are reserved. Permission is hereby granted to all who desire to use this source code in their programs provided the above copyright is included in the program.